package com.basics.common.utils;


import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

public class SnowFlake {
    private static final long TWEPOCH = 12888349746579L;
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATACENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;
    private static final long WORKER_ID_SHIFT = 12L;
    private static final long DATACENTER_ID_SHIFT = 17L;
    private static final long TIMESTAMP_LEFT_SHIFT = 22L;
    private static final long SEQUENCE_MASK = 4095L;
    private static long lastTimestamp = -1L;
    private long sequence = 0L;
    private long workerId = 1L;
    private long processId = 1L;
    private static SnowFlake snowFlake = new SnowFlake();

    public static synchronized long nextId() {
        return snowFlake.getNextId();
    }

    private SnowFlake() {
        this.workerId = this.getMachineNum();
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        this.processId = Long.valueOf(runtimeMXBean.getName().split("@")[0]);
        long workerMask = 31L;
        this.workerId &= workerMask;
        long processMask = 31L;
        this.processId &= processMask;
    }

    public synchronized long getNextId() {
        long timestamp = this.timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
        } else {
            if (lastTimestamp == timestamp) {
                this.sequence = this.sequence + 1L & 4095L;
                if (this.sequence == 0L) {
                    timestamp = this.tilNextMillis(lastTimestamp);
                }
            } else {
                this.sequence = 0L;
            }

            lastTimestamp = timestamp;
            return timestamp - 12888349746579L << 22 | this.processId << 17 | this.workerId << 12 | this.sequence;
        }
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp;
        for(timestamp = this.timeGen(); timestamp <= lastTimestamp; timestamp = this.timeGen()) {
        }

        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    private long getMachineNum() {
        StringBuilder sb = new StringBuilder();

        Enumeration e;
        try {
            e = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException var5) {
            var5.printStackTrace();
            return 1L;
        }

        while(e.hasMoreElements()) {
            NetworkInterface ni = (NetworkInterface)e.nextElement();
            sb.append(ni.toString());
        }

        long machinePiece = (long)sb.toString().hashCode();
        return machinePiece;
    }
}

